﻿import struct
import re
from pyparsing import *
from wbox.gx import opcode
from wbox.gx.utils import devinfo



# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   

class Operand(object):
    def __init__(self, items):
        self.items = items
        
    def pack(self, dword=False):
        L = []
        for item in self.items:
            data = item.pack(dword).rstrip('\x00')
            if len(data) < 2:
                data += '\x00'
            size = chr(len(data) + 2)
            L.append(size + data + size)
        return "".join(L)
        
    @property
    def mnemonic(self):
        for item in self.items:
            if item.code < 0xf0:
                return item.mnemonic
       
    def __repr__(self):
        return "<{0}: {1}>".format(self.__class__.__name__, repr(self.items))
        
    def __str__(self):
        L = []
        for item in self.items:
            s = str(item)
            L.append(s)
        return "".join(L)
    
    
class OpItem(object):
    def __init__(self, mnemonic, code, value):
        self.mnemonic = mnemonic
        self.code = code
        self.value = value
        
    def __repr__(self):
        return "<{0}: {1}>".format(self.__class__.__name__, str(self))
        
    def __str__(self):
        return "{0}{1}".format(self.mnemonic, repr(self.value))
    
class OpDec(OpItem):
    def pack(self, dword=False):
        fmt = "<Bh" if not dword else "<Bl"
        return struct.pack(fmt, self.code, self.value)
    
class OpHex(OpItem):
    def pack(self, dword=False):
        fmt = "<BH" if not dword else "<BL"
        return struct.pack(fmt, self.code, self.value)
            
    def __str__(self):
        hex = "{0:X}".format(self.value)
        if hex[0] in "ABCDEF":
            hex = "0" + hex
        return "{0}{1}".format(self.mnemonic, hex)
    
class OpFloat(OpItem):
    def pack(self, dword=False):
        return struct.pack("<Bf", self.code, self.value)
    
class OpString(OpItem):
    def pack(self, dword=False):
        return struct.pack("B", self.code) + self.value
        
    def __str__(self):
        return '"{0}"'.format(self.value)
        
class OpDevice(OpItem):
    _prefix_  = ["K", "U", "J", "BL", "@"]
    _postfix_ = [".", "V", "Z", "ZZ"]
    _sep_     = ["U", "J", "BL"]
    
    def isPrefix(self):
        return (self.code >= 0xF0) and (self.mnemonic in self._prefix_)
    
    def isPostfix(self):
        return (self.code >= 0xF0) and (self.mnemonic in self._postfix_)
        
    def hasSep(self):
        return (self.code >= 0xF0) and (self.mnemonic in self._sep_)
        
class OpDecDevice(OpDec, OpDevice):
    def pack(self, dword=False):
        fmt = "<Bh" if (self.mnemonic != "ZR") else "<Bl"
        return struct.pack(fmt, self.code, self.value)
    
class OpHexDevice(OpHex, OpDevice):
    def pack(self, dword=False):
        return struct.pack("<Bh", self.code, self.value)
        
class OpIndex(OpDecDevice):
    def pack(self, dword=False):
        return struct.pack("<Bh", self.code, self.value)
        
        
def operandAction(s, l, t):
    return Operand(t[:])
   
def constAction(s, l, t):
    mnemonic = t[0][0]
    code, base, bit = devinfo.getCode(mnemonic)
    if mnemonic in "KJ":
        op = OpDec(mnemonic, code, int(t[1]))
    elif mnemonic in "HU":
        op = OpHex(mnemonic, code, int(t[1], 16))
    elif mnemonic == "E":
        op = OpFloat(mnemonic, code, float(t[1]))
    elif mnemonic == '"':
        op = OpString(mnemonic, code, t[0][1:-1])
    else:
        raise Exception("unknown device mnemonic")
    return op
    
def deviceAction(s, l, t):
    mnemonic = t[0]
    code, base, bit = devinfo.getCode(mnemonic)
    if base == 10:
        op = OpDecDevice(mnemonic, code, int(t[1]))
    else: # base == 16
        op = OpHexDevice(mnemonic, code, int(t[1], 16))
    return op
        
def indexAction(s, l, t):
    mnemonic = t[0]
    code, base, bit = devinfo.getExtCode(mnemonic)
    return OpIndex(mnemonic, code, int(t[1]))
  
def prefixAction(s, l, t):
    mnemonic = t[0]
    code, base, bit = devinfo.getExtCode(mnemonic)
    if mnemonic in "KJBL":
        op = OpDecDevice(mnemonic, code, int(t[1]))
    elif mnemonic == "U":
        op = OpHexDevice(mnemonic, code, int(t[1], 16))
    else:
        raise Exception("unknown device mnemonic")
    return op
    
def atAction(s, l, t):
    mnemonic = t[0]
    code, base, bit = devinfo.getExtCode(mnemonic)
    return OpDecDevice(mnemonic, code, 0)
    
def postfixAction(s, l, t):
    mnemonic = t[0]
    code, base, bit = devinfo.getExtCode(mnemonic)
    if mnemonic == ".":
        op = OpHexDevice(mnemonic, code, int(t[1], 16))
    else:
        raise Exception("unknown device mnemonic")
    return op
    
def indexingAction(s, l, t):
    if isinstance(t[-1], OpIndex):
        t[-2], t[-1] = t[-1], t[-2]
        
def swapAction(s, l, t):
    t[-2], t[-1] = t[-1], t[-2]

        
HEX         = Regex(r"[0-9A-F]+")
DEC         = Regex(r"[+-]?[0-9]+")
FLOAT       = Regex(r"([+-]?[0-9]+(\.[0-9]*)?)|(\.[0-9]+)")
SEP         = Regex(r"[\\\\/]").suppress()
END         = (White() | StringEnd()).suppress()

STRING      = dblQuotedString.setParseAction(constAction)
X           = (CaselessLiteral("X")  + HEX).setParseAction(deviceAction)
Y           = (CaselessLiteral("Y")  + HEX).setParseAction(deviceAction)
M           = (CaselessLiteral("M")  + DEC).setParseAction(deviceAction)
SM          = (CaselessLiteral("SM") + DEC).setParseAction(deviceAction)
L           = (CaselessLiteral("L")  + DEC).setParseAction(deviceAction)
T           = (CaselessLiteral("T")  + DEC).setParseAction(deviceAction)
D           = (CaselessLiteral("D")  + DEC).setParseAction(deviceAction)
SD          = (CaselessLiteral("SD") + DEC).setParseAction(deviceAction)
R           = (CaselessLiteral("R")  + DEC).setParseAction(deviceAction)
ZR          = (CaselessLiteral("ZR") + DEC).setParseAction(deviceAction)
B           = (CaselessLiteral("B")  + HEX).setParseAction(deviceAction)
W           = (CaselessLiteral("W")  + HEX).setParseAction(deviceAction)
K           = (CaselessLiteral("K")  + DEC).setParseAction(constAction)
H           = (CaselessLiteral("H")  + HEX).setParseAction(constAction)
E           = (CaselessLiteral("E")  + FLOAT).setParseAction(constAction)
V           = (CaselessLiteral("V")  + DEC).setParseAction(deviceAction)
Z           = (CaselessLiteral("Z")  + DEC).setParseAction(deviceAction)
FX          = (CaselessLiteral("FX") + DEC).setParseAction(deviceAction)
FY          = (CaselessLiteral("FY") + DEC).setParseAction(deviceAction)
FD          = (CaselessLiteral("FD") + DEC).setParseAction(deviceAction)
U           = (CaselessLiteral("U")  + HEX).setParseAction(constAction)
J           = (CaselessLiteral("J")  + DEC).setParseAction(constAction)
G           = (CaselessLiteral("G")  + DEC).setParseAction(deviceAction)
S           = (CaselessLiteral("S")  + DEC).setParseAction(deviceAction)
P           = (CaselessLiteral("P")  + DEC).setParseAction(deviceAction)
INDEX       = ((CaselessLiteral("V") | CaselessLiteral("ZZ") | CaselessLiteral("Z")) + DEC).setParseAction(indexAction)
NETWORK     = (CaselessLiteral("J")  + DEC).setParseAction(prefixAction)
MODULE      = (CaselessLiteral("U")  + HEX).setParseAction(prefixAction)
BLOCK       = (CaselessLiteral("BL") + DEC).setParseAction(prefixAction)
DIGIT       = (CaselessLiteral("K")  + DEC).setParseAction(prefixAction)
DOT         = (CaselessLiteral(".")  + HEX).setParseAction(postfixAction)


const       = ((K | H | E | U | J | STRING) + Optional(INDEX) + END).setParseAction(indexingAction).leaveWhitespace()
network     = (NETWORK + Optional(INDEX)).setParseAction(indexingAction)
module      = (MODULE  + Optional(INDEX)).setParseAction(indexingAction)
indirect    = Literal("@").setParseAction(atAction)
bit_device  = (M | SM | L | X | Y | B | T | V | P)
word_device = (D | SD | W | R | ZR | Z)
func_device = (FD | FX | FY)
bit_device  = bit_device \
            | (word_device + DOT).setParseAction(swapAction)
word_device = word_device \
            | (DIGIT + bit_device) \
            | (indirect + word_device) \
            | (module + SEP + G) \
            | (network + SEP + (X | Y | B | W)) \
            | (BLOCK + SEP + S)
device      = func_device | ((bit_device | word_device) + Optional(INDEX)).setParseAction(indexingAction)
operand     = (const | device).setParseAction(operandAction)
operands    = ZeroOrMore(operand)

space_re = re.compile("\s+")
def parseop(op):
    # 공백을 모두 없애고, 대문자로 바꾼다.
    #op = space_re.sub("", op).upper()
    return operand.parseString(op)[0]
        
def parseops(op):
    #print repr(op)
    return operands.parseString(op)
    
def testOperand():
    devices = """\
K1
K1Z0
HA
HAZ0
E1
E1.
E.1
E.0
M0
K1M0
K1M0Z0
D0
D0.0
@D0
@D0Z0
M0Z0
D0Z0
ZR0ZZ0
U0
J1
"Wolf System"
U0/G0
U0Z0/G0
U0Z0/G0Z0
FX0
FY0
FD0
BL0/S0
""".splitlines()
    for device in devices:
        op = operands.parseString(device)[0]
        print op, repr(op.pack())
    print operands.parseString('K1 K1M0 "Wolf System" D0')
    
    
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


if __name__ == '__main__':
    testOperand()
    